package server

import (
	"crypto/tls"
	"fmt"
	"sync"

	. "github.com/go-mysql-org/go-mysql/mysql"
)

var defaultServer = NewDefaultServer()

type Server struct {
	serverVersion string
	protocolVersion int
	capability uint32
	collationId       uint8
	defaultAuthMethod string
	pubKey           []byte
	tlsConfig        *tls.Config
	cacheShaPassword *sync.Map
}

func NewDefaultServer() *Server {
	caPem, caKey := generateCA()
	certPem, keyPem := generateAndSignRSACerts(caPem, caKey)
	tlsConf := NewServerTLSConfig(caPem, certPem, keyPem, tls.VerifyClientCertIfGiven)
	return &Server{
		serverVersion:   "5.7.0",
		protocolVersion: 10,
		capability: CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 |
			CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH | CLIENT_SSL | CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA,
		collationId:       DEFAULT_COLLATION_ID,
		defaultAuthMethod: AUTH_NATIVE_PASSWORD,
		pubKey:            getPublicKeyFromCert(certPem),
		tlsConfig:         tlsConf,
		cacheShaPassword:  new(sync.Map),
	}
}

func NewServer(serverVersion string, collationId uint8, defaultAuthMethod string, pubKey []byte, tlsConfig *tls.Config) *Server {
	if !isAuthMethodSupported(defaultAuthMethod) {
		panic(fmt.Sprintf("server authentication method '%s' is not supported", defaultAuthMethod))
	}

	var capFlag = CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | CLIENT_PROTOCOL_41 |
		CLIENT_TRANSACTIONS | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH | CLIENT_CONNECT_ATTRS |
		CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
	if tlsConfig != nil {
		capFlag |= CLIENT_SSL
	}
	return &Server{
		serverVersion:     serverVersion,
		protocolVersion:   10,
		capability:        capFlag,
		collationId:       collationId,
		defaultAuthMethod: defaultAuthMethod,
		pubKey:            pubKey,
		tlsConfig:         tlsConfig,
		cacheShaPassword:  new(sync.Map),
	}
}

func isAuthMethodSupported(authMethod string) bool {
	return authMethod == AUTH_NATIVE_PASSWORD || authMethod == AUTH_CACHING_SHA2_PASSWORD || authMethod == AUTH_SHA256_PASSWORD
}

func (s *Server) InvalidateCache(username string, host string) {
	s.cacheShaPassword.Delete(fmt.Sprintf("%s@%s", username, host))
}
